/*
 jChecs: a Java chess game sample 

 Copyright (C) 2006-2011 by David Cotton

 This program is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free Software
 Foundation; either version 2 of the License, or (at your option) any later
 version.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

 You should have received a copy of the GNU General Public License along with
 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
 Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package fr.free.jchecs.ai;

import fr.free.jchecs.core.MoveGenerator;
import fr.free.jchecs.core.Piece;
import fr.free.jchecs.core.PieceType;
import fr.free.jchecs.core.Square;

import static fr.free.jchecs.ai.AbstractEngine.MATE_VALUE;
import static fr.free.jchecs.core.Constants.FILE_COUNT;
import static fr.free.jchecs.core.Constants.RANK_COUNT;

/**
 * Fonction d'évaluation basée sur le matériel et la position des pièces
 * présentes sur le plateau.
 * <p>
 * Classe sûre vis-à-vis des threads.
 * </p>
 * 
 * @author David Cotton
 */
final class BoardControlHeuristic implements Heuristic {
	/** Identifiant de la classe pour la sérialisation. */
	private static final long serialVersionUID = -7163145298434616262L;

	/**
	 * Bonus/Malus d'un cavalier (blanc par défaut) en fonction de sa position.
	 */
	private static final int[] KNIGHT_POSITIONS = { //
	-50, -30, -30, -30, -30, -30, -30, -50, // a1 ... h1
			-30, -20, -20, -20, -20, -20, -20, -30, // a2 ... h2
			-20, 0, 20, 20, 20, 20, 0, -20, // a3 ... h3
			-20, 0, 20, 20, 20, 20, 0, -20, // a4 ... h4
			-20, 0, 10, 20, 20, 10, 0, -20, // a5 ... h5
			-20, 0, 10, 10, 10, 10, 0, -20, // a6 ... h6
			-20, -10, 0, 0, 0, 0, -10, -20, // a7 ... h7
			-40, -20, -20, -20, -20, -20, -20, -40, // a8 ... h8
	};
	static {
		assert KNIGHT_POSITIONS.length == 64;
	}

	/**
	 * Bonus/Malus d'un pion (blanc par défaut) en fonction de sa position.
	 */
	private static final int[] PAWN_POSITIONS = { 0, 0, 0, 0, 0, 0, 0, 0, // a1
			// ...
			// h1
			2, 2, 2, -2, -2, 2, 2, 2, // a2 ... h2
			-2, -2, -2, 4, 4, -2, -2, -2, // a3 ... h3
			0, 0, 0, 4, 4, 0, 0, 0, // a4 ... h4
			2, 4, 6, 8, 8, 6, 4, 2, // a5 ... h5
			4, 6, 8, 10, 10, 8, 6, 4, // a6 ... h6
			4, 6, 8, 10, 10, 8, 6, 4, // a7 ... h7
			500, 500, 500, 500, 500, 500, 500, 500, // a8 ... h8
	};
	static {
		assert PAWN_POSITIONS.length == 64;
	}

	/**
	 * Bonus/Malus de base liés à la position d'une pièce (symétrique : adapté
	 * aux deux couleurs).
	 */
	private static final int[] DEFAULT_POSITIONS = { 0, 0, 0, 0, 0, 0, 0, 0, // a1
			// ...
			// h1
			0, 0, 0, 5, 5, 0, 0, 0, // a2 ... h2
			0, 0, 5, 5, 5, 5, 0, 0, // a3 ... h3
			0, 5, 5, 10, 10, 5, 5, 0, // a4 ... h4
			0, 5, 5, 10, 10, 5, 5, 0, // a5 ... h5
			0, 0, 5, 5, 5, 5, 0, 0, // a6 ... h6
			0, 0, 0, 5, 5, 0, 0, 0, // a7 ... h7
			0, 0, 0, 0, 0, 0, 0, 0, // a8 ... h8
	};
	static {
		assert DEFAULT_POSITIONS.length == 64;
	}

	/**
	 * Crée une nouvelle instance.
	 */
	BoardControlHeuristic() {
		// Rien de spécifique...
	}

	/**
	 * Renvoi la valeur estimée d'un état du jeu, pour les fonctions de
	 * recherche du meilleur coup.
	 * 
	 * @param pEtat
	 *            Etat du jeu.
	 * @param pTrait
	 *            Positionné à "true" si l'on veut une évaluation du point de
	 *            vue des blancs.
	 * @return Valeur estimée.
	 * @see Heuristic#evaluate(MoveGenerator,boolean)
	 */
	@Override
	public int evaluate(final MoveGenerator pEtat, final boolean pTrait) {
		assert pEtat != null;

		int res = -pEtat.getHalfmoveCount();

		for (final Square s : Square.values()) {
			final Piece piece = pEtat.getPieceAt(s);
			if (piece != null) {
				final boolean traitPiece = piece.isWhite();
				final PieceType typePiece = piece.getType();
				final int val = typePiece.getValue();
				final int pos;
				switch (typePiece) {
				case BISHOP:
				case QUEEN:
				case ROOK:
					pos = DEFAULT_POSITIONS[s.getIndex()];
					break;
				case KING:
					if ((traitPiece != pTrait)
							&& (pEtat.getFullmoveNumber() > 10)
							&& pEtat.isInCheck(traitPiece)) {
						if (pEtat.getValidMoves(traitPiece).length == 0) {
							// Malus pour un mat...
							pos = MATE_VALUE;
						} else {
							// Malus pour un échec...
							pos = -250;
						}
					} else {
						// Pas de valeur de position pour le roi.
						pos = 0;
					}
					break;
				case KNIGHT:
					if (traitPiece) {
						pos = KNIGHT_POSITIONS[s.getIndex()];
					} else {
						pos = KNIGHT_POSITIONS[((RANK_COUNT - 1) - s.getRank())
								* FILE_COUNT + s.getFile()];
					}
					break;
				case PAWN:
					if (traitPiece) {
						pos = PAWN_POSITIONS[s.getIndex()];
					} else {
						pos = PAWN_POSITIONS[((RANK_COUNT - 1) - s.getRank())
								* FILE_COUNT + s.getFile()];
					}
					break;
				default:
					assert false;
					pos = 0;
				}
				final int score = val + pos;
				if (traitPiece == pTrait) {
					res += score;
				} else {
					res -= score;
				}
			}
		}

		return res;
	}
}
